/** \file
* Implementation of the XML web reader class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/xml/XmlUrlSource.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#include "config.h"

#include "system/DebugConsole.hpp"
#include "xml/XmlUrlSource.hpp"

#include <cstring>
#include <fstream>

using namespace std;


XmlUrlSource::XmlUrlSource(XmlTree *treeInit, const char *pathInit,
	CurlGlob *curlGlobInit) :
	XmlSource(treeInit, pathInit),
	curlglob(curlGlobInit),
	xmlthread(*this, &XmlUrlSource::xmlProcess)
{
	bytes = 0;
	bytesAllocated = 0;
	buffer = 0;

	curleasy = curl_easy_init();
	if (!curleasy)
	{
		PDEBUG("curl_easy_init() failed");
		return;
	}
	CURLcode result;
	if (result = curl_easy_setopt(curleasy, CURLOPT_WRITEFUNCTION,
		curlWriteData))
	{
		PDEBUG_CURL("curl_easy_setopt CURLOPT_WRITEFUNCTION failed", result);
		return;
	}
	if (result = curl_easy_setopt(curleasy, CURLOPT_WRITEDATA, this))
	{
		PDEBUG_CURL("curl_easy_setopt CURLOPT_WRITEDATA failed", result);
		return;
	}
	if (result = curl_easy_setopt(curleasy, CURLOPT_SSL_VERIFYPEER, false))
	{
		PDEBUG_CURL("curl_easy_setopt CURLOPT_SSL_VERIFYPEER failed", result);
		return;
	}
//	if (result = curl_easy_setopt(curleasy, CURLOPT_SSH_AUTH_TYPES,
//		CURLSSH_AUTH_PASSWORD))
//	{
//		PDEBUG_CURL("curl_easy_setopt CURLOPT_SSH_AUTH_TYPES failed", result);
//		return;
//	}
	if (result = curl_easy_setopt(curleasy, CURLOPT_USERPWD,
		"chorustest:betricky"))
	{
		PDEBUG_CURL("curl_easy_setopt CURLOPT_USERPWD failed", result);
		return;
	}

	if (result = curl_easy_setopt(curleasy, CURLOPT_SHARE,
		curlglob->getShare()))
	{
		PDEBUG_CURL("curl_easy_setopt CURLOPT_SHARE failed", result);
		return;
	}
}

XmlUrlSource::~XmlUrlSource()
{
	if (curleasy)
	{
		CURLcode result = curl_easy_setopt(curleasy, CURLOPT_SHARE, 0);
		if (result)
			PDEBUG_CURL("curl_easy_setopt failed", result);
		curl_easy_cleanup(curleasy);
		curleasy = 0;
	}
}

bool XmlUrlSource::perform()
{
	const char *cpath = path.c_str();
	CURLcode result;
	if (result = curl_easy_setopt(curleasy, CURLOPT_URL, cpath))
	{
		PDEBUG_CURL("curl_easy_setopt failed", result);
		return false;
	}

	if (!curlmutex.lock())
		return false;

	bool success = false;

	// Use a typical MTU for the initial buffer size
	bytesAllocated = 1500;
	buffer = new char[bytesAllocated];
	if (!buffer)
	{
		PDEBUG("new failed");
		goto fail;
	}

	if (!xmlthread.start())
		goto fail;

	result = curl_easy_perform(curleasy);
	if (result)
	{
		PDEBUG_CURL("curl_easy_perform failed", result);
		goto fail;
	}

	success = true;
fail:
	// Wait for the XML processing to end
	success &= xmlmutex.lock();

	// Free the buffer
	if (buffer)
	{
		delete [] buffer;
		buffer = 0;
	}

	// Signal curl end
	success &= curlmutex.unlock();
	// Allow the XML processing thread to exit
	success &= xmlmutex.unlock();

	return success;
}

size_t XmlUrlSource::curlWriteData(void *ibuffer, size_t size, size_t nmemb,
	void *userp)
{
	return ((XmlUrlSource*)userp)->xmlHandover((char*)ibuffer, size*nmemb);
}

size_t XmlUrlSource::xmlHandover(char *ibuffer, size_t ibytes)
{
	// Wait for the XML processing to end
	if (!xmlmutex.lock())
		return (size_t)-1;

	// Copy the buffer

	bytes = ibytes;

	if (bytesAllocated < bytes)
	{
		bytesAllocated = bytes;
		delete [] buffer;
		buffer = new char[bytes];
		if (!buffer)
		{
			PDEBUG("new failed");
			return (size_t)-1;
		}
	}
	memcpy(buffer, ibuffer, bytes);

	// Signal curl end
	if (!curlmutex.unlock())
		return (size_t)-1;
	// Allow the XML processing to resume
	if (!xmlmutex.unlock())
		return (size_t)-1;
	// Restart curl
	if (!curlmutex.lock())
		return (size_t)-1;

	return bytes;
}

ofstream xml("xml.txt");

bool XmlUrlSource::xmlProcess()
{
	PDEBUG2("Processing URL source in this thread.", path);
	for (;;)
	{
		// Wait for curl to finish
		if (!curlmutex.lock())
			return false;
		// Signal XML start
		if (!xmlmutex.lock())
			return false;
		// Allow curl to resume
		if (!curlmutex.unlock())
			return false;

		// Check for end of transmission
		if (!buffer)
			return xmlmutex.unlock();

		// Process the data
		xml << path << ": \n";
		xml.write(buffer, bytes);
		xml << "\n\n";

		bool success = writeData(buffer, bytes);

		// Signal XML end
		success &= xmlmutex.unlock();

		if (!success)
			return false;
	}
}
